/**
 * 创建人:  @author huangpeng    
 * 创建时间:  2023年11月05日 15:42
 * 项目名称:  lemon-blog-cloud
 * 文件名称:  BiMailServiceImpl
 * 文件描述:  @Description: 邮件service实现类
 * <p>
 * All rights Reserved, Designed By huangpeng 
 * @Copyright: 2021-2023
 *
 */
package com.lemon.message.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.lemon.common.core.base.BaseServiceImpl;
import com.lemon.common.core.constant.Constants;
import com.lemon.common.core.constant.MailTemplateConstants;
import com.lemon.common.core.constant.SecurityConstants;
import com.lemon.common.core.domain.Result;
import com.lemon.common.core.enums.ResultEnums;
import com.lemon.common.core.exception.ServiceException;
import com.lemon.common.core.utils.StringUtils;
import com.lemon.common.core.utils.file.FileUploadUtils;
import com.lemon.common.core.utils.file.NetFileUtils;
import com.lemon.common.core.utils.uuid.IdUtils;
import com.lemon.common.core.web.domain.AjaxResult;
import com.lemon.common.redis.service.RedisService;
import com.lemon.common.security.utils.OptEntityUtils;
import com.lemon.message.domain.BiEmail;
import com.lemon.message.domain.BiEmailTemplate;
import com.lemon.message.mapper.BiEmailMapper;
import com.lemon.message.mapper.BiEmailTemplateMapper;
import com.lemon.message.service.BiEmailService;
import com.lemon.message.service.feign.SystemFeignClient;
import com.lemon.system.api.RemoteFileService;
import com.lemon.system.api.domain.BiFileInfo;
import com.lemon.system.api.domain.BiUser;
import com.lemon.system.api.model.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.DataSource;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 包名称：com.lemon.message.service.impl
 * 类名称：BiMailServiceImpl
 * 类描述：邮件service实现类
 * 创建人：@author huangpeng
 * 创建时间：2023年11月05日 15:42
 */
@Service
public class BiMailServiceImpl extends BaseServiceImpl<BiEmail> implements BiEmailService {

    public static final Logger logger = LoggerFactory.getLogger(BiMailServiceImpl.class);

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private BiEmailMapper biEmailMapper;

    @Autowired
    private BiEmailTemplateMapper biEmailTemplateMapper;

    @Autowired
    private SystemFeignClient systemFeignClient;

    @Autowired
    private RemoteFileService remoteFileService;

    @Autowired
    private RedisService redisService;

    /**
     * 邮件发送方邮箱
     */
    @Value("${spring.mail.username}")
    private String fromEmail;

    /**
     * 邮件服务器地址
     */
    @Value("${spring.mail.host}")
    private String smtpid;

    @Override
    public int insert(BiEmail model) {
        OptEntityUtils.preInsert(model);
        return biEmailMapper.insertSelective(model);
    }

    @Override
    public int update(BiEmail model) {
        OptEntityUtils.preUpdate(model);
        return biEmailMapper.updateByPrimaryKeySelective(model);
    }

    @Override
    public List<BiEmail> selectBiEmailList(BiEmail biEmail) {
        return biEmailMapper.selectBiEmailList(biEmail);
    }

    @Async("EmailAsync")
    @Override
    public CompletableFuture<Boolean> sendEmail(BiEmail model) {
        checkEmail(model);
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper;
        model.setFromEmail(fromEmail);
        model.setSmtpid(smtpid);
        try {
            helper = new MimeMessageHelper(message, true);
            //true代表支持多组件，如附件，图片等
            helper.setFrom(fromEmail);
            helper.setTo(model.getToEmail().split(";"));
            helper.setSubject(model.getSubject());
            helper.setText(model.getContent(), true);
            // 抄送人
            if (StrUtil.isNotEmpty(model.getCcEmail())) {
                helper.setCc(model.getCcEmail().split(";"));
            }
            // 密送人
            if (StrUtil.isNotEmpty(model.getBccEmail())) {
                helper.setBcc(model.getBccEmail().split(";"));
            }
            // 添加附件，可多次调用该方法添加多个附件
            // url网络附件
            List<String> attachList = new ArrayList<>();
            if (StrUtil.isEmpty(model.getAttachUrl())) {
                if (CollUtil.isNotEmpty(model.getAttachUrlList())) {
                    for (String fileUrl : model.getAttachUrlList()) {
                        File file = NetFileUtils.getNetUrl(fileUrl);
                        // 获取文件名称
                        Result<BiFileInfo> fileInfoResult = remoteFileService.getFileInfoByUrl(fileUrl);
                        if (Objects.equals(ResultEnums.ERROR.getCode(), fileInfoResult.getCode()) || fileInfoResult.getData() == null) {
                            throw new ServiceException("获取附件信息失败！");
                        }
                        helper.addAttachment(fileInfoResult.getData().getFileName(), file);
                        attachList.add(fileUrl);
                    }
                }
                model.setAttachUrl(CollUtil.join(attachList, Constants.STR_SEMI));
            } else {
                for (String fileUrl : model.getAttachUrl().split(Constants.STR_SEMI)) {
                    File file = NetFileUtils.getNetUrl(fileUrl);
                    // 获取文件名称
                    Result<BiFileInfo> fileInfoResult = remoteFileService.getFileInfoByUrl(fileUrl);
                    if (Objects.equals(ResultEnums.ERROR.getCode(), fileInfoResult.getCode()) || fileInfoResult.getData() == null) {
                        throw new ServiceException("获取附件信息失败！");
                    }
                    helper.addAttachment(fileInfoResult.getData().getFileName(), file);
                }
            }
            // 本地附件
            if (CollUtil.isNotEmpty(model.getAttachFileList())) {
                for (MultipartFile multipartFile : model.getAttachFileList()) {
                    helper.addAttachment(FileUploadUtils.getFullFileName(multipartFile), multipartFile);
                }
            }
            if (CollUtil.isNotEmpty(model.getInlineFileList())) {
                // 内联 inline 附件
                for (MultipartFile multipartFile : model.getInlineFileList()) {
                    helper.addInline(FileUploadUtils.getFullFileName(multipartFile), (DataSource) multipartFile);
                }
            }
            mailSender.send(message);
            logger.info("发送附件邮件成功");
            model.setDvSendStatus(Constants.STR_ONE);
            OptEntityUtils.preInsert(model);
            biEmailMapper.insertSelective(model);
            return CompletableFuture.completedFuture(true);
        } catch (Exception e) {
            logger.error("发送附件邮件失败", e);
            model.setDvSendStatus(Constants.STR_ZERO);
            OptEntityUtils.preInsert(model);
            biEmailMapper.insertSelective(model);
            return CompletableFuture.completedFuture(false);
        }
    }

    @Override
    public String sendCaptcha(Map<String, Object> userMap) {
        String toEmail = String.valueOf(userMap.get("email"));
        String uuid = IdUtils.simpleUUID();
        Result<LoginUser> biUserResult = systemFeignClient.getInfoByEmail(toEmail, SecurityConstants.INNER);
        if (Objects.equals(ResultEnums.ERROR.getCode(), biUserResult.getCode()) || biUserResult.getData() == null) {
            throw new ServiceException("用户信息不存在！");
        }
        AjaxResult templateCodeResult = systemFeignClient.getElValueByElCodeAndFCode(MailTemplateConstants.ELEMENT_CODE_LOGIN_CAPTCHA_TEMPLATE,
                MailTemplateConstants.FUNCTION_CODE);
        String templateCode = String.valueOf(templateCodeResult.getData());
        if (Objects.equals(ResultEnums.ERROR.getCode(), templateCodeResult.getCode()) || templateCodeResult.getData() == null
                || Objects.equals(templateCode, "") || Objects.equals(templateCode, "null")) {
            throw new ServiceException("登陆验证码邮件对应的邮件模板编码未配置！");
        }

        BiUser biUser = biUserResult.getData().getBaseUser();
        BiEmailTemplate emailTemplate = biEmailTemplateMapper.selectByTemplateCode(templateCode);
        if (emailTemplate == null) {
            throw new ServiceException("邮件模板不存在");
        }
        // 处理邮件模板中的占位符，验证码除外
        handlePlaceholder(emailTemplate, BeanUtil.beanToMap(biUser));

        // 获取6位验证码,并替换验证码
        String captcha = StringUtils.generateCaptcha(3);
        emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{captcha}}", captcha));
        // 替换邮件中的收件人邮箱
        emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{toEmail}}", toEmail));

        // 收件人
        if (StrUtil.isEmpty(emailTemplate.getToEmail())) {
            emailTemplate.setToEmail(toEmail);
        } else {
            emailTemplate.setToEmail(emailTemplate.getToEmail().replaceAll("\\{\\{toEmail}}", toEmail));
        }

        // 抄送人
        if (StrUtil.isEmpty(emailTemplate.getCcEmail())) {
            emailTemplate.setCcEmail(null);
        } else {
            emailTemplate.setCcEmail(emailTemplate.getCcEmail().replaceAll("\\{\\{ccEmail}}", ""));
        }

        // 密送人
        if (StrUtil.isEmpty(emailTemplate.getBccEmail())) {
            emailTemplate.setBccEmail(null);
        } else {
            emailTemplate.setBccEmail(emailTemplate.getBccEmail().replaceAll("\\{\\{bccEmail}}", ""));
        }

        BiEmail biEmail = new BiEmail();
        BeanUtil.copyProperties(emailTemplate, biEmail);
        CompletableFuture<Boolean> result = sendEmail(biEmail);
        try {
            if (result.get()) {
                // 保存验证码信息
                String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
                redisService.setCacheObject(verifyKey, captcha, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
                return uuid;
            }
        } catch (InterruptedException | ExecutionException e) {
            logger.error("邮件发送失败，失败原因：{}", e.getMessage(), e);
            throw new ServiceException("邮件发送失败");
        }
        throw new ServiceException("邮件发送失败,请联系系统管理员");
    }

    @Override
    public String sendRegisterCaptcha(Map<String, Object> userMap) {
        String toEmail = String.valueOf(userMap.get("email"));
        String uuid = IdUtils.simpleUUID();
        AjaxResult templateCodeResult = systemFeignClient.getElValueByElCodeAndFCode(MailTemplateConstants.ELEMENT_CODE_REGISTER_CAPTCHA_TEMPLATE,
                MailTemplateConstants.FUNCTION_CODE);
        String templateCode = String.valueOf(templateCodeResult.getData());
        if (Objects.equals(ResultEnums.ERROR.getCode(), templateCodeResult.getCode()) || templateCodeResult.getData() == null
                || Objects.equals(templateCode, "") || Objects.equals(templateCode, "null")) {
            throw new ServiceException("注册验证码邮件对应的邮件模板编码未配置！");
        }

        BiUser biUser = new BiUser();
        BiEmailTemplate emailTemplate = biEmailTemplateMapper.selectByTemplateCode(templateCode);
        if (emailTemplate == null) {
            throw new ServiceException("邮件模板不存在");
        }
        // 处理邮件模板中的占位符，验证码除外
        handlePlaceholder(emailTemplate, BeanUtil.beanToMap(biUser));

        // 获取6位验证码,并替换验证码
        String captcha = StringUtils.generateCaptcha(3);
        emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{captcha}}", captcha));
        // 替换邮件中的收件人邮箱
        emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{toEmail}}", toEmail));

        // 收件人
        if (StrUtil.isEmpty(emailTemplate.getToEmail())) {
            emailTemplate.setToEmail(toEmail);
        } else {
            emailTemplate.setToEmail(emailTemplate.getToEmail().replaceAll("\\{\\{toEmail}}", toEmail));
        }

        // 抄送人
        if (StrUtil.isEmpty(emailTemplate.getCcEmail())) {
            emailTemplate.setCcEmail(null);
        } else {
            emailTemplate.setCcEmail(emailTemplate.getCcEmail().replaceAll("\\{\\{ccEmail}}", ""));
        }

        // 密送人
        if (StrUtil.isEmpty(emailTemplate.getBccEmail())) {
            emailTemplate.setBccEmail(null);
        } else {
            emailTemplate.setBccEmail(emailTemplate.getBccEmail().replaceAll("\\{\\{bccEmail}}", ""));
        }

        BiEmail biEmail = new BiEmail();
        BeanUtil.copyProperties(emailTemplate, biEmail);
        CompletableFuture<Boolean> result = sendEmail(biEmail);
        try {
            if (result.get()) {
                // 保存验证码信息
                String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
                redisService.setCacheObject(verifyKey, captcha, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
                return uuid;
            }
        } catch (InterruptedException | ExecutionException e) {
            logger.error("邮件发送失败，失败原因：{}", e.getMessage(), e);
            throw new ServiceException("邮件发送失败");
        }
        throw new ServiceException("邮件发送失败,请联系系统管理员");
    }

    @Override
    public String sendRestPasswordCaptcha(Map<String, Object> userMap) {
        String toEmail = String.valueOf(userMap.get("email"));
        String uuid = IdUtils.simpleUUID();
        AjaxResult templateCodeResult = systemFeignClient.getElValueByElCodeAndFCode(MailTemplateConstants.ELEMENT_CODE_REST_PASSWORD_CAPTCHA_TEMPLATE,
                MailTemplateConstants.FUNCTION_CODE);
        Result<LoginUser> biUserResult = systemFeignClient.getInfoByEmail(toEmail, SecurityConstants.INNER);
        if (Objects.equals(ResultEnums.ERROR.getCode(), biUserResult.getCode()) || biUserResult.getData() == null) {
            throw new ServiceException("用户信息不存在！");
        }
        String templateCode = String.valueOf(templateCodeResult.getData());
        if (Objects.equals(ResultEnums.ERROR.getCode(), templateCodeResult.getCode()) || templateCodeResult.getData() == null
                || Objects.equals(templateCode, "") || Objects.equals(templateCode, "null")) {
            throw new ServiceException("重置用户密码验证码邮件对应的邮件模板编码未配置！");
        }

        BiUser biUser = biUserResult.getData().getBaseUser();
        BiEmailTemplate emailTemplate = biEmailTemplateMapper.selectByTemplateCode(templateCode);
        if (emailTemplate == null) {
            throw new ServiceException("邮件模板不存在");
        }
        // 处理邮件模板中的占位符，验证码除外
        handlePlaceholder(emailTemplate, BeanUtil.beanToMap(biUser));

        // 获取6位验证码,并替换验证码
        String captcha = StringUtils.generateCaptcha(3);
        emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{captcha}}", captcha));
        // 替换邮件中的收件人邮箱
        emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{toEmail}}", toEmail));

        // 收件人
        if (StrUtil.isEmpty(emailTemplate.getToEmail())) {
            emailTemplate.setToEmail(toEmail);
        } else {
            emailTemplate.setToEmail(emailTemplate.getToEmail().replaceAll("\\{\\{toEmail}}", toEmail));
        }

        // 抄送人
        if (StrUtil.isEmpty(emailTemplate.getCcEmail())) {
            emailTemplate.setCcEmail(null);
        } else {
            emailTemplate.setCcEmail(emailTemplate.getCcEmail().replaceAll("\\{\\{ccEmail}}", ""));
        }

        // 密送人
        if (StrUtil.isEmpty(emailTemplate.getBccEmail())) {
            emailTemplate.setBccEmail(null);
        } else {
            emailTemplate.setBccEmail(emailTemplate.getBccEmail().replaceAll("\\{\\{bccEmail}}", ""));
        }

        BiEmail biEmail = new BiEmail();
        BeanUtil.copyProperties(emailTemplate, biEmail);
        CompletableFuture<Boolean> result = sendEmail(biEmail);
        try {
            if (result.get()) {
                // 保存验证码信息
                String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
                redisService.setCacheObject(verifyKey, captcha, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
                return uuid;
            }
        } catch (InterruptedException | ExecutionException e) {
            logger.error("邮件发送失败，失败原因：{}", e.getMessage(), e);
            throw new ServiceException("邮件发送失败");
        }
        throw new ServiceException("邮件发送失败,请联系系统管理员");
    }

    /**
     * 邮件模板占位符处理
     * @Title: handlePlaceholder
     * @Description: 邮件模板占位符处理
     * @param emailTemplate 邮件模板信息
     * @param dataMap 占位符源数据
     * @return: void
     * @throws:
     * @author: huangpeng
     * @Date: 2023/11/18 18:45
     */
    private void handlePlaceholder(BiEmailTemplate emailTemplate, Map<String, Object> dataMap) {
        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
            emailTemplate.setSubject(emailTemplate.getSubject().replaceAll("\\{\\{" + entry.getKey() + "}}", String.valueOf(entry.getValue())));
            emailTemplate.setContent(emailTemplate.getContent().replaceAll("\\{\\{" + entry.getKey() + "}}", String.valueOf(entry.getValue())));
        }
    }

    /**
     * 校验邮件
     * @Title: checkEmail
     * @Description: 校验邮件
     * @param model 邮件信息
     * @return: void
     * @throws:
     * @author: huangpeng
     * @Date: 2023/11/5 18:33
     */
    private void checkEmail(BiEmail model) {
        Assert.notNull(model,"邮件模板不能为空");
        Assert.hasText(model.getToEmail(), "邮件收件人不能为空");
        Assert.hasText(model.getSubject(), "邮件主题不能为空");
        Assert.hasText(model.getContent(), "邮件内容不能为空");
    }
}
